/**************************************************************************
* FXTranslate
* Copyright (C) 2009 thierry lorthiois (lorthiois@bbsoft.fr)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
**************************************************************************/


#include "FXTranslate.h"


FXIMPLEMENT(FXTranslate,FXTranslator,NULL,0)


FXTranslate::FXTranslate(FXApp* a) : FXTranslator(a)
{
}

FXTranslate::~FXTranslate()
{
	map.clear();
}


const FXchar* FXTranslate::tr(const FXchar*,const FXchar* message,const FXchar*) const
{
	const FXchar *str = map.find(message);
	if (str != NULL) 
		return str;
	else
		return message;
}


bool FXTranslate::load(const FXString &filename)
{
	if (!FXStat::exists(filename))
		return false;

	FXFile file(filename,FXIO::Reading);
	if(!file.isOpen())
		return false;

	map.clear();
	FXchar line[MAXBUFFER];
	FXchar msgid[MAXBUFFER];
	FXint bol,eol,end,name,value,p,lineno;

	lineno=bol=eol=end=0;
	while (readString(file,line,bol,eol,end)) {
		lineno++;

      // Skip leading spaces
      while (bol<eol && Ascii::isBlank(line[bol])) bol++;

		// Skip comment lines and empty lines
		if (bol>=eol || line[bol]=='#' || line[bol]==';' || line[bol]=='\n' || line[bol]=='\r')
			goto next;

		// Scan key name
		for (name=bol; bol<eol && line[bol]!='=' && !Ascii::isControl(line[bol]); bol++) ;

		// Check errors
		if (bol>=eol || line[bol]!='=') {
			fxwarning("%s:%d: expected '=' to follow key.\n",filename.text(),lineno);
			goto next;
		}

		// Remove trailing spaces after name
		for (p=bol; name<p && Ascii::isBlank(line[p-1]); p--) ;

		// Terminate name
		line[p]='\0';

		// Skip leading spaces
		for (bol++; bol<eol && Ascii::isBlank(line[bol]); bol++) ;

		// Scan value
		for (value=bol; bol<eol && !Ascii::isControl(line[bol]); bol++) ;

		// Remove trailing spaces after value
		for (p=bol; value<p && Ascii::isBlank(line[p-1]); p--) ;

		// Terminate value
		line[p]='\0';

		// Add entry to current section
		if (compare(line+name, "msgid") == 0)
			strcpy(msgid, dequote(line+value));
		else
			map.insert(msgid, dequote(line+value));

next: bol=eol;
	}

	return true;
}


bool FXTranslate::buildTranslation(const FXString &dir, const FXString &pattern, const FXString &filename)
{
	FXFile file(filename, FXIO::Writing);
	FXchar buffer[MAXBUFFER];

	if (!file.isOpen())
		return false;

	map.clear();
	browseDir(dir, pattern, file, buffer);
	file.close();
	return true;
}


bool FXTranslate::browseDir(const FXString &dir, const FXString &pattern, FXFile &file, FXchar *buffer)
{
   FXString *list;
	int nb;

	// list files
	nb = FXDir::listFiles(list, dir, pattern);
	for (int i=0; i < nb ; i++)
		storeMessage(file, buffer, dir+"/"+list[i]);
	if (list) delete[] list;

	// browse sub-dir
	nb = FXDir::listFiles(list, dir, "*", FXDir::NoFiles|FXDir::NoParent);
	for (int i=0; i < nb ; i++)
		browseDir(dir+"/"+list[i], pattern, file, buffer);
	if (list) delete[] list;
	return true;
}


bool FXTranslate::storeMessage(FXFile &file, FXchar *buffer, const FXString &filename)
{
	FXint bol, eol, end, value, p, nb, line;
	bol = eol = end = nb = line = 0;
	const FXchar *s;
	FXString str;

	FXFile fileSrc(filename, FXIO::Reading);
	if (!fileSrc.isOpen())
		return false;

	while (readString(fileSrc,buffer,bol,eol,end)) {
		line++;
		// Skip empty lines
		if (bol>=eol || buffer[bol]=='\n' || buffer[bol]=='\r')
			goto next;

scan:
		// Scan "tr"
		bol++;
		while (bol<eol && (buffer[bol-1]!='t' || buffer[bol]!='r')) bol++;
		if (bol>=eol) goto next;

      // Scan "("
		bol++;
      while (bol<eol && Ascii::isBlank(buffer[bol])) bol++;
		if (bol>=eol) goto next;
		if (buffer[bol]!='(') goto scan;

		// Scan Quote
		while (bol<eol && buffer[bol]!='\"' && buffer[bol]!=')') bol++;
		if (bol>=eol) goto next;
		if (buffer[bol]==')') goto scan;

		// extract message
		p = bol+1;
		while (p<eol && buffer[p]!='\"') p++;
		if (p>=eol) goto next;
		buffer[++p]='\0';

		// TODO : test traduction sur plusieurs lignes
		// check duplicate message
		s = map.find(buffer+bol);
		if (s != NULL) {
			bol = p;
			goto scan;
		}

		// Store message
		nb++;
		map.insert(buffer+bol, "");
		str = "msgid =";
		str += (buffer+bol);
		str += "\nmsgstr=\n";
		//str += (buffer+bol);
		//str += "\n";
		file.writeBlock(str.text(), str.length());

		bol = p;
		goto scan;

next:	bol=eol;
	}
	fileSrc.close();
	//printf("%s %d lines %d traductions\n", filename.text(), line, nb);
	return true;
}


void FXTranslate::mergeMessage(const FXString &fileSrc, const FXString &fileDest)
{
}


// ********************************************************************************
// following functions shared with FXSettings
bool FXTranslate::readString(FXFile& file,FXchar *buffer,FXint& bol,FXint& eol,FXint& end)
{
	register FXint n;
	do {
		if (eol>=end) {
			if (bol<end) { memmove(buffer,buffer+bol,end-bol); }
			end=end-bol;
			bol=0;
			eol=end;
			n=file.readBlock(buffer+end,MAXBUFFER-end);
			if (n<0) return false;
			end+=n;
		}
	}
	while (eol<end && buffer[eol++]!='\n');
	return bol<eol;
}


FXchar* FXTranslate::dequote(FXchar* text)
{
  register FXchar *result=text;
  register FXchar *ptr=text;
  register FXuint v;
  if(*text=='"'){
    text++;
    while((v=*text++)!='\0' && v!='\n' && v!='"'){
      if(v=='\\'){
        v=*text++;
        switch(v){
          case 'n':
            v='\n';
            break;
          case 'r':
            v='\r';
            break;
          case 'b':
            v='\b';
            break;
          case 'v':
            v='\v';
            break;
          case 'a':
            v='\a';
            break;
          case 'f':
            v='\f';
            break;
          case 't':
            v='\t';
            break;
          case '\\':
            v='\\';
            break;
          case '"':
            v='"';
            break;
          case '\'':
            v='\'';
            break;
          case 'x':
            v='x';
            if(Ascii::isHexDigit(*text)){
              v=Ascii::digitValue(*text++);
              if(Ascii::isHexDigit(*text)){
                v=(v<<4)+Ascii::digitValue(*text++);
                }
              }
            break;
          case '0':
          case '1':
          case '2':
          case '3':
          case '4':
          case '5':
          case '6':
          case '7':
            v=v-'0';
            if('0'<=*text && *text<='7'){
              v=(v<<3)+*text++-'0';
              if('0'<=*text && *text<='7'){
                v=(v<<3)+*text++-'0';
                }
              }
            break;
          }
        }
      *ptr++=v;
      }
    *ptr='\0';
    }
  return result;
  }


